﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace DTU_Test;

/// <summary>
/// IO地址信息,只适用于单个Modbus数据点的配置，不是使用多个数据点的配置文件。
/// 如：多个寄存器连续读取时，不适用此类。
/// 因此主要的功能码：01，03，05，06
/// </summary>
public class ModbusDataPoint
{
    /// <summary>
    /// 测点名称
    /// </summary>
    public string? Name { get; set; }

    /// <summary>
    /// 站号
    /// </summary>

    public byte SlaveId { get; set; } = 1;

    /// <summary>
    /// 功能码
    /// </summary>
    public byte FunctionCode { get; set; }

    /// <summary>
    /// 寄存器起始地址
    /// </summary>
    public ushort Address { get; set; }

    /// <summary>
    /// 寄存器读取个数
    /// </summary>
    public ushort Count { get; set; } = 1;

    /// <summary>
    /// 值（如果是读取就是返回的值）
    /// </summary>
    public object? Value { get; set; }

    /// <summary>
    /// 值类型
    /// </summary>
    public DataTypeEnum ValueType { get; set; }

    /// <summary>
    /// 大小端类型
    /// </summary>
    public EndianEnum EndianType { get; set; }

    /// <summary>
    /// 倍率
    /// </summary>
    public float Rate { get; set; } = 1.0f;

    /// <summary>
    /// 描叙
    /// </summary>
    public string? Description { get; set; }

    /// <summary>
    /// 解析Modbus数据点的值
    /// </summary>
    /// <param name="data">字节数组</param>
    /// <returns></returns>
    public bool ParseData(byte[] data)
    {
        // 这里可以根据data解析Modbus数据点的值
        // 目前示例中没有实现具体的解析逻辑
        if (data == null || data.Length == 0) return false;

        if (data[0] == this.SlaveId && data[1] == this.FunctionCode)// 确认是正确的从站和功能码
        {
            // 根据ValueType和EndianType解析数据
            switch (this.ValueType)
            {
                case DataTypeEnum.UINT16:
                    if (data.Length >= 5)
                    {
                        ushort value = (ushort)((data[3] << 8) | data[4]);
                        this.Value = value * this.Rate;
                    }
                    break;

                case DataTypeEnum.INT16:
                    if (data.Length >= 5)
                    {
                        short value = (short)((data[3] << 8) | data[4]);
                        this.Value = value * this.Rate;
                    }
                    break;

                case DataTypeEnum.UINT32:
                    if (data.Length >= 7)
                    {
                        uint value = (uint)((data[3] << 24) | (data[4] << 16) | (data[5] << 8) | data[6]);
                        this.Value = value * this.Rate;
                    }
                    break;

                case DataTypeEnum.INT32:
                    if (data.Length >= 7)
                    {
                        int value = (int)((data[3] << 24) | (data[4] << 16) | (data[5] << 8) | data[6]);
                        this.Value = value * this.Rate;
                    }
                    break;

                case DataTypeEnum.Float:
                    if (data.Length >= 7)
                    {
                        if (EndianType == EndianEnum.BigEndian)
                        {
                            // BigEndian
                            byte[] floatBytes = new byte[4] { data[6], data[5], data[4], data[3] };
                            float value = BitConverter.ToSingle(floatBytes, 0);
                            this.Value = value * this.Rate;
                        }
                        else
                        {
                            // LittleEndian
                            byte[] floatBytes = new byte[4] { data[3], data[4], data[5], data[6] };
                            float value = BitConverter.ToSingle(floatBytes, 0);
                            this.Value = value * this.Rate;
                        }
                        
                    }
                    break;

                case DataTypeEnum.Double:
                    if (data.Length >= 11)
                    {
                        if (EndianType == EndianEnum.BigEndian)
                        {
                            // BigEndian
                            byte[] doubleBytes = new byte[8] { data[10], data[9], data[8], data[7], data[6], data[5], data[4], data[3] };
                            double value = BitConverter.ToDouble(doubleBytes, 0);
                            this.Value = value * this.Rate;
                        }
                        else
                        {
                            // LittleEndian
                            byte[] doubleBytes = new byte[8] { data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10] };
                            double value = BitConverter.ToDouble(doubleBytes, 0);
                            this.Value = value * this.Rate;
                        }
                        
                    }
                    break;

                case DataTypeEnum.Bool:
                    if (data.Length >= 5)
                    {
                        bool value = data[3] != 0;
                        this.Value = value;
                    }
                    break;

                case DataTypeEnum.UByte:
                    if (data.Length >= 5)
                    {
                        byte value = data[3];
                        this.Value = value * this.Rate;
                    }
                    break;

                case DataTypeEnum.Byte:
                    if (data.Length >= 5)
                    {
                        sbyte value = (sbyte)data[3];
                        this.Value = value * this.Rate;
                    }
                    break;
                // 可以添加更多类型的解析逻辑
                default:
                    return false;
            }
            return true;
        }
        return false;
    }


    /// <summary>
    /// 根据发送的字节数组解析Modbus数据点
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public static ModbusDataPoint Parse(byte[] data)
    {
        if (data == null || data.Length < 5) return null;
        ModbusDataPoint dp = new ModbusDataPoint
        {
            SlaveId = data[0],
            FunctionCode = data[1],
            Address = (ushort)(data[2] << 8 | data[3]),
            Count = (ushort)(data[4] << 8 | data[5]),
            ValueType = DataTypeEnum.UINT16, // 默认类型，可以根据实际情况调整
            EndianType = EndianEnum.BigEndian // 默认大小端，可以根据实际情况调整
        };

        return dp;
    }

    /// <summary>
    /// 转换成字节
    /// </summary>
    /// <returns></returns>
    public byte[] ToBytes()
    {
        if (this.FunctionCode == 1 || this.FunctionCode == 2 || this.FunctionCode == 3 || this.FunctionCode == 4)
        {
            var data = new byte[] { SlaveId, FunctionCode, this.Address.ToBytes()![1], this.Address.ToBytes()![0], this.Count.ToBytes()[1], this.Count.ToBytes()[0] };
            var crc = data.ToCRCModbus();
            List<byte> bytes = new List<byte>();
            bytes.AddRange(data);
            bytes.AddRange(crc);
            return bytes.ToArray();
        }
        else if (this.FunctionCode == 5 || this.FunctionCode == 6)
        {
            switch (this.ValueType)
            {
                case DataTypeEnum.Bool:
                    if (bool.TryParse(this.Value.ToString(), out bool b))
                    {
                        var valueBytes = BitConverter.GetBytes(b ? (ushort)0xFF00 : (ushort)0x0000);
                        var data = new byte[] { SlaveId, FunctionCode, this.Address.ToBytes()![1], this.Address.ToBytes()![0], valueBytes[1], valueBytes[0] };
                        var crc = data.ToCRCModbus();
                        List<byte> bytes = new List<byte>();
                        bytes.AddRange(data);
                        bytes.AddRange(crc);
                        return bytes.ToArray();
                    }
                    break;

                case DataTypeEnum.UByte:
                case DataTypeEnum.UINT16:
                    {
                        if (ushort.TryParse(this.Value.ToString(), out ushort v))
                        {
                            var valueBytes = BitConverter.GetBytes(v); //后面再考虑EndianType
                            var data = new byte[] { SlaveId, FunctionCode, this.Address.ToBytes()![1], this.Address.ToBytes()![0], valueBytes[1], valueBytes[0] };
                            var crc = data.ToCRCModbus();
                            List<byte> bytes = new List<byte>();
                            bytes.AddRange(data);
                            bytes.AddRange(crc);
                            return bytes.ToArray();
                        }
                    }
                    break;

                case DataTypeEnum.Byte:
                case DataTypeEnum.INT16:
                    {
                        if (short.TryParse(this.Value.ToString(), out short v))
                        {
                            var valueBytes = BitConverter.GetBytes(v); //后面再考虑EndianType
                            var data = new byte[] { SlaveId, FunctionCode, this.Address.ToBytes()![1], this.Address.ToBytes()![0], valueBytes[1], valueBytes[0] };
                            var crc = data.ToCRCModbus();
                            List<byte> bytes = new List<byte>();
                            bytes.AddRange(data);
                            bytes.AddRange(crc);
                            return bytes.ToArray();
                        }
                    }
                    break;
            }
        }

        return null;
    }

    public override string ToString()
    {
        return $"变量Name:{Name},Address:{Address},Value:{Value},ValueType:{ValueType},Endian:{EndianType}";
    }
}

public enum EndianEnum
{
    [Display(Name = "None")] None = 0,
    [Display(Name = "BigEndian")] BigEndian,
    [Display(Name = "LittleEndian")] LittleEndian,
    [Display(Name = "BigEndianSwap")] BigEndianSwap,
    [Display(Name = "LittleEndianSwap")] LittleEndianSwap
}

public enum DataTypeEnum
{
    [Display(Name = "bit")]
    Bit,

    [Display(Name = "bool")]
    Bool,

    [Display(Name = "uint8")]
    UByte,

    [Display(Name = "int8")]
    Byte,

    [Display(Name = "uint16")]
    UINT16,

    [Display(Name = "int16")]
    INT16,

    [Display(Name = "bcd16")]
    Bcd16,

    [Display(Name = "uint32")]
    UINT32,

    [Display(Name = "int32")]
    INT32,

    [Display(Name = "float")]
    Float,

    [Display(Name = "bcd32")]
    Bcd32,

    [Display(Name = "uint64")]
    UINT64,

    [Display(Name = "int64")]
    INT64,

    [Display(Name = "double")]
    Double,

    [Display(Name = "ascii")]
    AsciiString,

    [Display(Name = "utf8")]
    Utf8String,

    [Display(Name = "datetime")]
    DateTime,

    [Display(Name = "timestamp(ms)")]
    TimeStampMs,

    [Display(Name = "timestamp(s)")]
    TimeStampS,

    [Display(Name = "Any")]
    Any,

    [Display(Name = "Custom1")]
    Custome1,

    [Display(Name = "Custom2")]
    Custome2,

    [Display(Name = "Custom3")]
    Custome3,

    [Display(Name = "Custom4")]
    Custome4,

    [Display(Name = "Custom5")]
    Custome5,

    [Display(Name = "Gb2312")]
    Gb2312String,
}